package com.demo.ex;


import com.demo.common.Result;
import com.demo.enums.RestFulStatus;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
import lombok.extern.log4j.Log4j2;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingRequestHeaderException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.net.ConnectException;

/**
 * 全局异常处理
 * @Author liuyj
 * @Date 2019-11-07 18:50:46
 */
@ControllerAdvice
@Log4j2(topic = "business")
public class GlobalExceptionHandler {


	/**
	 * 这三种方式有什么区别呢？
	 * logger.error(e.getMessage());
	 * 打印的只有异常的错误信息，如果异常没有抛出错误信息，则显示的就是null
	 * logger.error(“发生了错误:”+e);
	 * 打印的是异常的类和异常的错误信息，这个也只比上面多了一个异常的类名
	 * logger.error(“发生了错误:”,e);
	 * 推荐使用这种，会完整的将e打印出来
	 */

	private static final String ERROR_MSG = "参数%s不能为空！";

	/**
	 * 参数不能为空
	 */
	@ExceptionHandler(MissingServletRequestParameterException.class)
	@ResponseBody
    public Result bindException(MissingServletRequestParameterException exception) {
		log.error("参数不能为空:",exception);
		return  Result.error(400, String.format(ERROR_MSG, exception.getParameterName()));
    }

	/**
	 * 参数校验错误
	 */
	@ExceptionHandler(MethodArgumentNotValidException.class)
	@ResponseBody
	public Result bindException(MethodArgumentNotValidException exception) {
		FieldError fieldError = exception.getBindingResult().getFieldError();
		log.error("参数校验错误:",exception);
		return Result.error(400, String.format("参数校验错误:%s", fieldError.getDefaultMessage()));
	}

	//注释掉具体错误异常，解决等保测试出的2个高危漏洞：
	// 目标URL存在框架注入漏洞 http://localhost/ntocc-site-api/home/orderList?pageNum=50%23%2A%2F--%3E%27%22%29%3B%3E%3CIFRAME%2FSRC%3Dhttp%3A%2F%2Fwww.baidu.com%2Fzqpuf.html%3E
	// 目标URL存在链接注入漏洞 http://localhost/ntocc-site-api/home/orderList?pageSize=50%23%2A%2F--%3E%27%22%29%3B%3E%3Ca%2Fhref%3D%2Flgtrb.html%3ELinkInjTest%3C%2Fa%3E
	/**
	 * 参数校验错误
	 */
	@ExceptionHandler(BindException.class)
	@ResponseBody
	public Result bindException(BindException exception) {
		FieldError fieldError = exception.getBindingResult().getFieldError();
		fieldError.getRejectedValue();
		log.error("参数校验错误:{}", fieldError.getDefaultMessage());
		return Result.error(400, String.format("参数校验错误:%s", fieldError.getDefaultMessage()));
	}






	/**
	 * 缺少参数异常
	 * @param exception
	 * @return
	 */
	@ExceptionHandler(MissingRequestHeaderException.class)
	@ResponseBody
	public Result bindException(MissingRequestHeaderException exception) {
		log.error("缺少参数异常:",exception);
		return Result.error(400, String.format(ERROR_MSG,  exception.getHeaderName()));
	}

	/**
	 * 缺少body异常
	 * @param exception
	 * @return
	 */
	@ExceptionHandler(HttpMessageNotReadableException.class)
	@ResponseBody
	public Result bindException(HttpMessageNotReadableException exception) {
		log.error("缺少body异常:",exception);
		return Result.error(400, String.format("缺少request body！",  exception.getMessage()));
	}

	/**
	 * 请求方式异常
	 * @param exception
	 * @return
	 */
	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
	@ResponseBody
	public Result bindException(HttpRequestMethodNotSupportedException exception) {
		log.error("请求方式异常:",exception);
		return Result.error(400, String.format("请求方式异常",  exception.getMessage()));
	}

	/**
	 * 校验参数异常
	 * @param exception
	 * @return
	 */
	@ExceptionHandler(ValidationException.class)
	@ResponseBody
    public Result bindException(ValidationException exception) {
		log.error("校验参数异常:",exception);
		if(exception instanceof ConstraintViolationException) {
			return Result.error(400, String.format(ERROR_MSG, ((ConstraintViolationException) exception).getConstraintViolations()));
		}
		return Result.error(400, String.format(ERROR_MSG, exception.getCause()));
    }
	
	@ExceptionHandler(ConstraintViolationException.class)
	@ResponseBody
    public Result bindException(ConstraintViolationException exception) {
		log.error("参数校验错误:",exception);
		StringBuffer message = new StringBuffer();
		for(ConstraintViolation<?> entity : exception.getConstraintViolations()) {
			if(message.length()>0) {
				message.append(";");
			}
			message.append(entity.getMessage());
		}
		return Result.error(400, message.toString());
    }
	
	/**
	 * 异常处理
	 */
	@ExceptionHandler(ServiceException.class)
	@ResponseBody
    public Result bindException(HttpServletRequest request, ServiceException exception) {
		log.error(exception);
		return Result.error(RestFulStatus.INTERNAL_SERVER_ERROR.value(), exception.getMessage());
    }
	@ExceptionHandler(NoHandlerFoundException.class)
	public Result handlerNoFoundException(Exception e) {
		log.error(e.getMessage(), e);
		return Result.error(404, "路径不存在，请检查路径是否正确");
	}

	@ExceptionHandler(DuplicateKeyException.class)
	public Result handleDuplicateKeyException(DuplicateKeyException e){
		log.error(e.getMessage(), e);
		return Result.error("数据库中已存在该记录");
	}

	@ExceptionHandler(UsernameIsExitedException.class)
	public Result usernameIsExitedException(UsernameIsExitedException e){
		log.error(e.getMessage(), e);
		return Result.error("用户已经存在");
	}

	@ExceptionHandler(ConnectException.class)
	public Result connectException(ConnectException e){
		log.error(e.getMessage(), e);
		return Result.error("系统调用异常");
	}

	@ExceptionHandler(ResourceAccessException.class)
	public Result connectException(ResourceAccessException e){
		log.error(e.getMessage(), e);
		return Result.error("系统之间调用异常");
	}

	@ResponseBody
	@ExceptionHandler(value = {ExpiredJwtException.class})
	public Result expiredJwtException(ExpiredJwtException e) {
		log.error(e.getMessage(), e);
		return Result.error("Token过期");
	}

	@ExceptionHandler(value = UnsupportedJwtException.class)
	@ResponseBody
	public Result unsupportedJwtException(UnsupportedJwtException e) {
		log.error(e.getMessage(), e);
		return Result.error("Token签名失败");
	}

	@ExceptionHandler(value = SignatureException.class)
	@ResponseBody
	public Result signatureException(SignatureException e) {
		log.error(e.getMessage(), e);
		return Result.error("Token格式错误");
	}

	@ExceptionHandler(value = IllegalArgumentException.class)
	@ResponseBody
	public Result illegalArgumentException(IllegalArgumentException e) {
		log.error(e.getMessage(), e);
		return Result.error("Token非法参数异常");
	}

	@ExceptionHandler(value = AccessDeniedException.class)
	@ResponseBody
	public Result accessDeniedException(AccessDeniedException e) {
		log.error(e.getMessage(), e);
		return Result.error("Token非法参数异常");
	}

	@ExceptionHandler(value = MalformedJwtException.class)
	@ResponseBody
	public Result malformedJwtException(MalformedJwtException e) {
		log.error(e.getMessage(), e);
		return Result.error("Token没有被正确构造");
	}

    /**
	 * 异常处理
	 */
//	@ExceptionHandler(Exception.class)
//	@ResponseBody
//    public Result bindException(HttpServletRequest request, Exception exception) {
//		log.error(exception);
//		return Result.error(RestFulStatus.INTERNAL_SERVER_ERROR);
//    }
}